/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. ******************************************************************************/ package org.apache.olingo.odata2.core.rest; import java.util.List; import javax.servlet.ServletConfig; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Encoded; import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.core.Application; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.PathSegment; import javax.ws.rs.core.Request; import javax.ws.rs.core.UriInfo; import org.apache.olingo.odata2.api.ODataServiceFactory; import org.apache.olingo.odata2.api.exception.ODataBadRequestException; import org.apache.olingo.odata2.api.exception.ODataException; import org.apache.olingo.odata2.core.exception.ODataRuntimeException; import org.apache.olingo.odata2.core.rest.app.AbstractODataApplication; /** * Default OData root locator responsible to handle the whole path and delegate all calls to a sub locator:<p> * <code>/{odata path} e.g. http://host:port/webapp/odata.svc/$metadata</code><br> * All path segments defined by a servlet mapping belong to the odata uri. * </p> * This behavior can be changed:<p> * <code>/{custom path}{odata path} e.g. http://host:port/webapp/bmw/odata.svc/$metadata</code><br> * The first segment defined by a servlet mapping belong to customer context and the following segments are OData * specific. * </p> * */ @Path("/") public class ODataRootLocator { @Context private HttpHeaders httpHeaders; @Context private UriInfo uriInfo; @Context private Request request; @Context private ServletConfig servletConfig; @Context private HttpServletRequest servletRequest; @Context private Application app; /** * Default root behavior which will delegate all paths to a ODataLocator. * @param pathSegments URI path segments - all segments have to be OData * @param xHttpMethod HTTP Header X-HTTP-Method for tunneling through POST * @param xHttpMethodOverride HTTP Header X-HTTP-Method-Override for tunneling through POST * @return a locator handling OData protocol * @throws ODataException * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ @Path("/{pathSegments: .*}") public Object handleRequest( @Encoded @PathParam("pathSegments") final List<PathSegment> pathSegments, @HeaderParam("X-HTTP-Method") final String xHttpMethod, @HeaderParam("X-HTTP-Method-Override") final String xHttpMethodOverride) throws ODataException, ClassNotFoundException, InstantiationException, IllegalAccessException { if (xHttpMethod != null && xHttpMethodOverride != null) { /* * X-HTTP-Method-Override : implemented by CXF * X-HTTP-Method : implemented in ODataSubLocator:handlePost */ if (!xHttpMethod.equalsIgnoreCase(xHttpMethodOverride)) { throw new ODataBadRequestException(ODataBadRequestException.AMBIGUOUS_XMETHOD); } } if (servletRequest.getPathInfo() == null) { return handleRedirect(); } ODataServiceFactory serviceFactory = getServiceFactory(); int pathSplit = getPathSplit(); final SubLocatorParameter param = new SubLocatorParameter(); param.setServiceFactory(serviceFactory); param.setPathSegments(pathSegments); param.setHttpHeaders(httpHeaders); param.setUriInfo(uriInfo); param.setRequest(request); param.setServletRequest(servletRequest); param.setPathSplit(pathSplit); return ODataSubLocator.create(param); } public ODataServiceFactory getServiceFactory() { return createServiceFactoryFromContext(app, servletRequest, servletConfig); } public int getPathSplit() { int pathSplit = 0; final String pathSplitAsString = servletConfig.getInitParameter(ODataServiceFactory.PATH_SPLIT_LABEL); if (pathSplitAsString != null) { pathSplit = Integer.parseInt(pathSplitAsString); } return pathSplit; } public static ODataServiceFactory createServiceFactoryFromContext(final Application app, final HttpServletRequest servletRequest, final ServletConfig servletConfig) { try { Class<?> factoryClass; if (app instanceof AbstractODataApplication) { factoryClass = ((AbstractODataApplication) app).getServiceFactoryClass(); } else { final String factoryClassName = servletConfig.getInitParameter(ODataServiceFactory.FACTORY_LABEL); if (factoryClassName == null) { throw new ODataRuntimeException("Servlet config missing: " + ODataServiceFactory.FACTORY_LABEL); } ClassLoader cl = (ClassLoader) servletRequest.getAttribute(ODataServiceFactory.FACTORY_CLASSLOADER_LABEL); if (cl == null) { factoryClass = Class.forName(factoryClassName); } else { factoryClass = Class.forName(factoryClassName, true, cl); } } return (ODataServiceFactory) factoryClass.newInstance(); } catch (Exception e) { throw new ODataRuntimeException("Exception during ODataServiceFactory creation occured.", e); } } private Object handleRedirect() { return new ODataRedirectLocator(); } }